//-*- c++ -*-
//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  CVD::image.h                                                        //
//                                                                      //
//  Definitions for of template class CVD::Image, fast_image		//
//                                                                      //
//  derived from IPRS_* developed by Tom Drummond                       //
//	Reworked to provide class heirachy and memory managementby E. Rosten//
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#ifndef CVD_IMAGE_H
#define CVD_IMAGE_H

#include <string.h>
#include <cvd/image_ref.h>
#include <cvd/exceptions.h>
#include <string>
#include <utility>
#include <type_traits>
#include <iterator>


namespace CVD {

namespace Exceptions {

  /// @ingroup gException
  namespace Image {
      /// Base class for all Image_IO exceptions
        /// @ingroup gException
        struct All: public CVD::Exceptions::All {};

        /// Input images have incompatible dimensions
        /// @ingroup gException
        struct IncompatibleImageSizes : public All {
            IncompatibleImageSizes(const std::string & function)
            {
                what = "Incompatible image sizes in " + function;
            };
        };

        /// Input ImageRef not within image dimensions
        /// @ingroup gException
        struct ImageRefNotInImage : public All {
            ImageRefNotInImage(const std::string & function)
            {
                what = "Input ImageRefs not in image in " + function;
            };
        };
    }
}

#ifndef DOXYGEN_IGNORE_INTERNAL
namespace Internal
{
	template<class C> struct ImagePromise
	{};
};
#endif

#ifdef CVD_IMAGE_DEBUG
	#define CVD_IMAGE_ASSERT(X,Y)  if(!(X)) throw Y()
	#define CVD_IMAGE_ASSERT2(X,Y,Z)  if(!(X)) throw Y(Z)
#else
	#define CVD_IMAGE_ASSERT(X,Y)
	#define CVD_IMAGE_ASSERT2(X,Y,Z)
#endif

/// Fatal image errors (used for debugging). These are not included in the
/// main CVD::Exceptions namespace since they are fatal errors which are 
/// only thrown if the library is compiled with <code>-D CVD_IMAGE_DEBUG</code>.
/// This compiles in image bounds checking (see CVD::BasicImage::operator[]())
/// and makes image accesses very slow!
/// @ingroup gException
namespace ImageError
{
	/// An attempt was made to access a pixel outside the image. Note that this is
	/// not derived from CVD::Exceptions::All.
	/// @ingroup gException
	class AccessOutsideImage{};
}


namespace Internal
{
	template<class T> inline void memfill(T* data, int n, const T val)
	{
		T* de = data + n;
		for(;data < de; data++)
			*data=val;
	}

	template<> inline void memfill(unsigned char* data, int n, const unsigned char val)
	{
		memset(data, val, n);
	}

	template<> inline void memfill(signed char* data, int n, const signed char val)
	{
		memset(data, val, n);
	}

	template<> inline void memfill(char* data, int n, const char val)
	{
		memset(data, val, n);
	}


	//Detecting a typedef...
	template<class C>
	struct IsDummy
	{
		//Fake function to pretend to return an instance of a type
		template<class S>
		static const C& get();

		//Two typedefs guaranteed to have different sizes
		typedef char One[1];
		typedef char Two[2];


		//Class to give us a size 2 return value or fail on 
		//substituting S
		template<int S> 
		struct SFINAE_dummy
		{
			typedef Two Type;
		};

		static One& detect_dummy(...);

		template<class S>
		static typename SFINAE_dummy<sizeof(typename S::dummy)>::Type& detect_dummy(const S&);

		static const bool Is = (sizeof(detect_dummy(get<C>()))==2);
	};
}


template<class T> class BasicImageIterator
{
	public:
		//Make it look like a standard iterator
		typedef std::forward_iterator_tag iterator_category;
		typedef T value_type;
		typedef std::ptrdiff_t difference_type;
		typedef T* pointer;
		typedef T& reference;

		const BasicImageIterator& operator++()
		{
			ptr++;
			if(ptr == row_end)
			{
				ptr += row_increment;
				row_end += total_width;

				if(ptr >= end)
					end = NULL;
			}
			return *this;
		}

		void operator++(int)
		{
			operator++();
		}
	
		T* operator->() const { return ptr; }
		T& operator*() const { return *ptr;}

		bool operator<(const BasicImageIterator& s) const 
		{ 
			//It's illegal to iterate _past_ end(), so < is equivalent to !=
			//for end iterators.
			if(is_end && s.is_end)
				return 0;
			else if(is_end)
				return s.end != NULL;
			else if(s.is_end) 
				return end != NULL; 
			else 
				return ptr < s.ptr; 
		}

		bool operator==(const BasicImageIterator& s) const 
		{ 
			return !((*this)!=s);
		}

		bool operator!=(const BasicImageIterator& s) const 
		{ 
			if(is_end && s.is_end)
				return 0;
			else if(is_end)
				return s.end != NULL;
			else if(s.is_end) 
				return end != NULL; 
			else 
				return ptr != s.ptr; 
		}



		BasicImageIterator()
		{}

		BasicImageIterator(T* start, int image_width, int row_stride, T* off_end)
		:ptr(start),
		 row_end(start + image_width), 
		 end(off_end), 
		 is_end(0),
		 row_increment(row_stride-image_width), 
		 total_width(row_stride)
		{ }

		//Prevent automatic conversion from a pointer (ie Image::iterator)
		explicit BasicImageIterator(T* end) 
		:ptr(end),is_end(1),row_increment(0),total_width(0)
		{ }

	protected:
		T* ptr;
		T *row_end, *end;
		bool is_end;
		int row_increment, total_width;
};

template<class C> class BasicImage;
template<class C> using SubImage = BasicImage<C>;

namespace Internal
{
	
	//The structure is a little funny. We use inheritance here as a way of conditional
	//namespacing. So think of it like that not subclassing :)
	//
	//Essentially there are two image "types". One is an image of pixels, and includes
	//all the per-pixel types like byte, Rgb<byte> float, etc. These have the properties
	//that there is precisely one element per pixel. That makes pixel access obvious and
	//straightforward, so slicing (sub images) works fine and so on. Types need not be POD
	//
	//The other type is for images of crud. This includes yuv422, yuv420p and MJPEG. These
	//have no easy way of accessing pixels in general and no wasy way of creating sub images.
	//Technically, the types like YUV422 can be sub-imaged and have strides, but I've got no
	//use case for that and no one wants it.
	//
	//Silly types have a typedef called "dummy" because they're a placeholder and so don't
	//actually represent the underlying data.
	//
	//Dummy types are usually of fixed size (e.g. yuv types) and so the size is/should be known
	//at compile time. In this case, the dummy struct must provide a std::ratio called bytes_per_pixel


	//This is the image data holder for silly types lilke 
	template<class T, bool D = Internal::IsDummy<T>::Is> class ImageData
	{
		public:

			ImageData(const ImageData&) = default;
			ImageData& operator=(const ImageData&)=default;
			
			ImageData(void* data, size_t len, ImageRef sz)
			:my_data(data),my_size(sz), data_length(len)
			{
			}

			ImageData(void* data, ImageRef sz)
			:my_data(data),my_size(sz), data_length(T::bytes_per_pixel::num*sz.area()/T::bytes_per_pixel::den)
			{
			}

			/// What is the row stride of the image?
			inline size_t datalength() const
			{
				return data_length;
			}
			
			ImageData()
			{
				my_data=0;
				my_size=ImageRef(0,0);
				data_length=0;
			}


		protected:

			void*    my_data;
			ImageRef my_size;
			size_t   data_length;

			using PointerType = void*;
			using ConstPointerType = const void*;
	};

	template<class T> class ImageData<T, false>
	{
		public:
			/// Construct an image from a block of data, assuming tight packing.
			/// @param data The image data in horizontal scanline order
			/// @param size The size of the image
			ImageData(T* data, const ImageRef& size)
			:my_data(data),my_size(size),my_stride(size.x)
			{
			}

			/// Construct an image from a block of data.
			/// @param data The image data in horizontal scanline order
			/// @param size The size of the image
			/// @param stride The row stride (or width, including the padding)
			ImageData(T* data, const ImageRef& size, int stride)
			:my_data(data),my_size(size),my_stride(stride)
			{
			}

			//We need them here in order to provide the functionality for
			//the asserts below. Technically they can apply to dummy types but
			//why bother?

			/// Is this pixel co-ordinate inside the image?
			/// @param ir The co-ordinate to test
			bool in_image(const ImageRef& ir) const
			{
				return ir.x >=0 && ir.y >=0 && ir.x < my_size.x && ir.y < my_size.y;
			}

			/// Is this pixel co-ordinate inside the image, and not too close to the edges?
			/// @param ir The co-ordinate to test
			/// @param border The size of the border: positive points inside the image.
			bool in_image_with_border(const ImageRef& ir, int border) const
			{
				return ir.x >=border && ir.y >=border && ir.x < my_size.x - border && ir.y < my_size.y - border;
			}

			/// Access a pixel from the image. Bounds checking is only performed if the library is compiled
			/// with <code>-D CVD_IMAGE_DEBUG</code>, in which case an ImageError::AccessOutsideImage exception is 
			/// thrown.
			inline T& operator[](const ImageRef& pos)
			{
				CVD_IMAGE_ASSERT(in_image(pos), ImageError::AccessOutsideImage);
				return (my_data[pos.y*my_stride + pos.x]);
			}
			
			/// Access a pixel from the image. Bounds checking is only performed if the library is compiled
			/// with <code>-D CVD_IMAGE_DEBUG</code>, in which case an ImageError::AccessOutsideImage exception is 
			/// thrown.
			inline const T& operator[](const ImageRef& pos) const 
			{
				CVD_IMAGE_ASSERT(in_image(pos), ImageError::AccessOutsideImage);
				return (my_data[pos.y*my_stride + pos.x]);
			}

			/// Access pointer to pixel row. Returns the pointer to the first element of the passed row.
			/// Allows to use [y][x] on images to access a pixel. Bounds checking is only performed if the library is compiled
			/// with <code>-D CVD_IMAGE_DEBUG</code>, in which case an ImageError::AccessOutsideImage exception is
			/// thrown.
			inline T* operator[](int row)
			{
				CVD_IMAGE_ASSERT(in_image(ImageRef(0,row)), ImageError::AccessOutsideImage);
				return my_data+row*my_stride;
			}

			/// Access pointer to pixel row. Returns the pointer to the first element of the passed row.
			/// Allows to use [y][x] on images to access a pixel. Bounds checking is only performed if the library is compiled
			/// with <code>-D CVD_IMAGE_DEBUG</code>, in which case an ImageError::AccessOutsideImage exception is
			/// thrown.
			inline const T* operator[](int row) const
			{
				CVD_IMAGE_ASSERT(in_image(ImageRef(0,row)), ImageError::AccessOutsideImage);
				return my_data+row*my_stride;
			}

			/// Given a pointer, this returns the image position as an ImageRef
			inline ImageRef pos(const T* ptr) const
			{
				int diff = ptr - my_data;
				return ImageRef(diff % my_stride, diff / my_size.x);
			}

			typedef BasicImageIterator<T> iterator;
			typedef BasicImageIterator<const T> const_iterator;

			/// The data type of the pixels in the image.
			typedef T value_type;
			
			/// Returns an iterator referencing the first (top-left) pixel in the image
			inline iterator begin()
			{
				return BasicImageIterator<T>(my_data, my_size.x, my_stride, end_ptr());
			}
			/// Returns a const iterator referencing the first (top-left) pixel in the image
			inline const_iterator begin() const
			{
				return BasicImageIterator<const T>(my_data, my_size.x, my_stride, end_ptr());
			}

			/// Returns an iterator pointing to one past the end of the image
			inline iterator end()
			{
				//Operator [] would always throw here!
				return BasicImageIterator<T>(end_ptr());
			}
			/// Returns a const iterator pointing to one past the end of the image
			inline const_iterator end() const
			{
				//Operator [] would always throw here!
				return BasicImageIterator<const T>(end_ptr());
			}


			/// What is the row stride of the image?
			inline int row_stride() const
			{
				return my_stride;
			}
			
			/// Return a sub image
			/// @param start Top left pixel of the sub image
			/// @param size width and  height of the sub image
			BasicImage<T> sub_image(const ImageRef& start, const ImageRef& size);

			/// Return a sub image
			/// @param start Top left pixel of the sub image
			/// @param size width and  height of the sub image
			const BasicImage<T> sub_image(const ImageRef& start, const ImageRef& size) const;

			ImageData(const ImageData&) = default;
			ImageData& operator=(const ImageData&)=default;

			ImageData()
			{
				my_data=nullptr;
				my_size = ImageRef(0,0);
				my_stride = 0;
			}

		protected:
				///Return an off-the-end pointer without ever throwing AccessOutsideImage
				T* end_ptr() { return my_data+my_size.y*my_stride; }

				///Return an off-the-end pointer without ever throwing AccessOutsideImage
				const T* end_ptr() const { return my_data+my_size.y*my_stride; }



				T* my_data;
				ImageRef my_size;
				int my_stride;	
				using PointerType = T*;
				using ConstPointerType = const T*;
	};

}

/// A generic image class to manage a block of arbitrarily padded data as an image. Provides
/// basic image access such as accessing a particular pixel co-ordinate. 
/// @param T The pixel type for this image. Typically either
/// <code>CVD::byte</code> or <code>CVD::Rgb<CVD::byte> ></code> are used,
/// but images could be constructed of any available type.
/// 
/// A BasicImage does not manage its own data, but provides access to an 
/// arbitrary externally-managed block of data as though it were an image. Use
/// the derived Image class if you want an image which also has its own data.
/// @ingroup gImage
template<class T> class BasicImage : public Internal::ImageData<T>
{
	static const bool IsDummy = Internal::IsDummy<T>::Is;
	protected:
		using Internal::ImageData<T>::my_size;
		using Internal::ImageData<T>::my_data;
		using typename Internal::ImageData<T>::PointerType;
		using typename Internal::ImageData<T>::ConstPointerType;


	public:
		
		//Inherit all constructors
		using Internal::ImageData<T>::ImageData;


		/// The image data is not destroyed when a BasicImage is destroyed.
		virtual ~BasicImage()
		{}


		/// Returns the raw image data
		inline ConstPointerType data() const
		{
			return my_data;
		}
		
		/// Returns the raw image data
		inline PointerType data()
		{
			return my_data;
		}

		inline void copy_from( const BasicImage<T> & other ){
			CVD_IMAGE_ASSERT2(other.size() == this->size(), Exceptions::Image::IncompatibleImageSizes, "copy_from");
			std::copy(other.begin(), other.end(), this->begin());
		}

		/// What is the size of this image?
		inline ImageRef size() const
		{
			return my_size;
		}

		///Set image data to all zero bytes. 
		///This only works on POD
		inline void  zero() 
		{
			static_assert(std::is_trivially_copyable<T>::value, "Error: zero() only works on POD types");
			for(int y=0; y < my_size.y; y++)
				memset((*this)[y], 0, sizeof(T) * my_size.x);
		}

		/// Set all the pixels in the image to a value. This is a relatively fast operation, using <code>memfill</code>.
		/// Safe to run on empty images.
		/// @param d The value to write into the image
		inline void fill(const T d)
		{
			for(int y=0; y < my_size.y; y++)
				Internal::memfill( (*this)[y], my_size.x, d);
		}

		/// Copy constructor
		/// @param copyof The image to copy
		BasicImage(const BasicImage& copyof)=default;
		
		/// Assignment operator
		/// @param copyof The image to copy
		BasicImage& operator=(const BasicImage&copyof)=default;

	protected:
		
		BasicImage()=default;

};


template<class C> BasicImage<C> Internal::ImageData<C, false>::sub_image(const ImageRef& start, const ImageRef& size)
{
	CVD_IMAGE_ASSERT(in_image(start), ImageError::AccessOutsideImage);
	CVD_IMAGE_ASSERT(in_image(start + size - ImageRef(1,1)), ImageError::AccessOutsideImage);
	return BasicImage<C>( &operator[](start), size, my_stride);
}

template<class C> const BasicImage<C> Internal::ImageData<C, false>::sub_image(const ImageRef& start, const ImageRef& size) const
{
	CVD_IMAGE_ASSERT(in_image(start), ImageError::AccessOutsideImage);
	CVD_IMAGE_ASSERT(in_image(start + size - ImageRef(1,1)), ImageError::AccessOutsideImage);

	C *ptr = my_data + start.y * my_stride + start.x;
	return BasicImage<C>( ptr , size, my_stride);
}

/// A full image which manages its own data.
/// @param T The pixel type for this image. Typically either
/// <code>CVD::byte</code> or <code>CVD::Rgb<CVD::byte> ></code> are used,
/// but images could be constructed of any available type.
///
/// The images behave much like 2D STL vectors. Iterators iterate over the pixels.
///
/// Loading and saving, format conversion and some copying functionality is
/// provided by external functions rather than as part of this class. See
/// the @ref gImageIO "Image loading and saving, and format conversion" module
/// for documentation of these functions.
/// @ingroup gImage
template<class T> 
class Image: public SubImage<T>
{
	private:
		struct CopyPlaceHolder
		{
			const Image* im;
		};

		using SubImage<T>::my_size;
		//using SubImage<T>::my_stride;
		using SubImage<T>::my_data;

	public:

		/// The data type of the pixels in the image.
		typedef T value_type;

		inline Image& copy_from( const SubImage<T> & other ){
			resize(other.size());
			std::copy(other.begin(), other.end(), this->begin());
			return *this;
		}

		///Copy constructor: new allocation and copy the data.
		///@param copy The image to copy
		Image(const Image& i)
		:Image(i.size())
		{
			copy_from(i);
		}
		

		///Move constructor: steal the pointer.
		Image(Image&& move_from)
		{
			static_cast<Internal::ImageData<T>&>(*this) = move_from;
			move_from.erase_fields();
		}

		/// Copy the data.
		///@param copyof The image to copy
		Image& operator=(const Image& i)
		{
			resize(i.size());
			copy_from(i);
			return *this;
		}

		Image& operator=(Image&& move_from)
		{
			static_cast<Internal::ImageData<T>&>(*this) = move_from;
			move_from.erase_fields();

			return *this;
		}


		
		#ifndef DOXYGEN_IGNORE_INTERNAL
		template<class C> const Image& operator=(Internal::ImagePromise<C> p)
		{
			p.execute(*this);
			return *this;
		}

		template<class C> Image(Internal::ImagePromise<C> p)
		:SubImage<T>()
		{
			p.execute(*this);
		}
		#endif
		
		///Default constructor: everything set to zero.
		Image()
		{
	
			erase_fields();
		}


		///Create an empty image of a given size.
		///@param size The size of image to create
		Image(const ImageRef& size)
		{
          resize(size);
		}

		///Create a filled image of a given size
		///@param size The size of image to create
		///@param val  The value to fill the image with
		Image(const ImageRef& size, const T& val)
		{
			resize(size);
			this->fill(val);
		}

		
		///Resize the image (destroying the data).
		///@param size The new size of the image
		void resize(const ImageRef& size)
		{	
			resize_(size, DD<Internal::IsDummy<T>::Is>{} );
		}

		///Resize the image (destroying the data). 
		//The resized image is filled with val.
		///@param size The new size of the image
		///@param val  The value to fill the image with
		void resize(const ImageRef& size, const T& val)
		{
			resize(size);
			fill(val);
		}

		///The destructor removes the image data
		~Image()
		{
			delete_old();
		}

		
	private:
		void delete_old()
		{
			delete_(DD<Internal::IsDummy<T>::Is>{});
			erase_fields();
		}
		
		void erase_fields()
		{
			static_cast<Internal::ImageData<T>&>(*this) = Internal::ImageData<T>();
		}
		
		//Allow type switching on dummy data: the internals are quite different
		//for dummy types.
		template<int Dummy> struct DD{};

		template<int Dummy> void resize_(const ImageRef& size, DD<Dummy>)
		{
			if(size == ImageRef(0,0))
			{
				delete_old();
			}
			else if(size != BasicImage<T>::my_size)
			{
				delete_old();
				static_cast<BasicImage<T>&>(*this) = BasicImage<T>(new T[size.area()], size);
			}
		}

		void resize_(const ImageRef& size, DD<true>)
		{
			if(size == ImageRef(0,0))
			{
				delete_old();
			}
			else if(size != BasicImage<T>::my_size)
			{
				delete_old();
				static_cast<BasicImage<T>&>(*this) = BasicImage<T>(nullptr, size);
				my_data = new char [this->datalength()];
			}
		}

		template<int Dummy> void delete_(DD<Dummy>)
		{
			delete[] my_data;
		}

		void delete_(DD<true>)
		{
			delete[] static_cast<char*>(my_data);
		}

};



} // end namespace
#endif
